#!/usr/bin/env python
# -*- coding: utf-8 -*- 
#representation of a crfsuite model

import re
import math
import numpy

#Regex for extracting all state and feature transitions
#gets the state transition weights
GET_STATE_WEIGHTS = re.compile(r'(?<=\(1\) )(?P<first>.) \-\-\> (?P<second>.): (?P<weight>[0-9]\.[0-9]+)')
#gets the feature weights
GET_FEATURE_WEIGHTS = re.compile(r'(?<=\(0\) )(?P<first>.+) \-\-\> (?P<second>.+): (?P<weight>[0-9]\.[0-9]+)')

class crfsuite_model(object):
    """A representation of a CRFSuite .model file.
    """
    def __init__(self, fileName):
        self.fileName = fileName
        modelFile = open(fileName, 'r')
        self.text = modelFile.read().decode('utf-8')
        modelFile.close()
        self.state_weights = GET_STATE_WEIGHTS.findall(self.text)
        self.feature_weights = GET_FEATURE_WEIGHTS.findall(self.text)

    @staticmethod
    def normalize_weights(weights):
        """Takes a set of weights which is a list of
        tuples (a,b,c) where
        a is the current state/observable
        b is the resulting state
        c is the CRF weight,
        and turns each weight into a normalized probability
        where P(a --> b) = exp(W(a --> b)) / Sum(exp(W(a --> B)))
        """
        CURR = 0 #the first member of the tuple is the current state/feature
        NEXT = 1 #the second member of the tuple is the next state
        WEIGHT = 2 #the third member of the tuple is the CRF weight
        currState = "" #the current state we are calculating the probability for
        nextState = "" #the next state
        mindex = 0 #how far the inner while loop reads into the weights
        maxdex = len(weights)
        transition = 0
        while transition < len(weights):
            currState = weights[transition][CURR]
            print currState
            maxdex = len(weights)
            total = 0
            for nextTransition in range(mindex, maxdex):
                nextState = weights[nextTransition][CURR]
                if nextState == currState:
                    total += math.exp(float(weights[nextTransition][WEIGHT]))
                    mindex += 1
                    transition += 1
                else:
                    mindex = nextTransition
                    maxdex = nextTransition
                    transition = nextTransition
            for nextTransition in range(transition, maxdex):
                if currState == "o":
                    print total
                newTransition = list(weights[nextTransition])
                newTransition[WEIGHT] = math.exp(float(weights[nextTransition][WEIGHT])) / total
                weights[nextTransition] = newTransition
                #print weights[nextTransition]
    
            
        
    
        
        
        


ALL = []

VOWELS = ['ɪ', 'e', 'i', 'ɛ', 'ɔ', 'ʊ', 'œ', 'ɑ', 'a', 'æ',
          'o', 'ɜ', 'ɵ', 'ʏ', 'u', 'ʉ', 'ɨ', 'ɒ', 'æ',
          'ʊ', 'ø', 'ɐ', 'ɶ', 'y', 'ɒ']


SCHWAS = ['ʌ', 'ə', 'ɚ']

VOWELS += SCHWAS

ALL += [VOWELS]

STOPS = ['b', 'ʔ', 'p', 'd', 't', 'ɡ', 'k', 'ɖ',
         'ɟ', 'ʦ', 'ʣ', 'ʧ', 'ʤ', 'c']

NASALS = ['m', 'n', 'ŋ', 'ɲ']

STOPS += NASALS

ALL += [STOPS]

FRICATIVES = ['ʃ', 'ʒ', 'ɕ', 's', 'h', 'x', 'f', 'ð', 'z', 'θ', 'ʝ',
              'v', 'χ', 'ç', 'β', 'ɣ', 'ɸ', 'ħ', 'ʁ', 'ʀ']

ALL += [FRICATIVES]

LIQUIDS = ['l', 'ɾ', 'ɹ', 'ɫ']

APPROXIMANTS = ['w', 'j', 'ʋ', 'ɥ', 'ʍ', 'ɻ', 'ʎ', 'ɭ']

LIQAPPROX = LIQUIDS #+ APPROXIMANTS

ALL += [LIQAPPROX]

NULL = ['-']

BOS = ['__BOS__']

EOS = ['__EOS__']

#ALL += [NULL]
FEAT = []
FEAT += [BOS]
FEAT += [EOS]

def fsa_weight(transitions, curr_symbols, next_symbols):
    CURRENT = 0
    NEXT = 1
    WEIGHT = 2
    total = 0
    for transition in transitions:
        if transition[CURRENT].encode('utf-8') in curr_symbols:
            if transition[NEXT].encode('utf-8') in next_symbols:
                total += math.exp(float(transition[WEIGHT]))
    return total

def print_all_weights(transitions, ALL = ALL):
    weights = numpy.zeros(len(ALL)) #matrix for each weight
    next_state_strs = []
    for group in range(0, len(ALL)):
        string = ""
        next_state_strs = []
        if group == 0: string += "Vowels --> "
        if group == 1: string += "Stops --> "
        if group == 2: string += "Fricatives --> "
        if group == 3: string += "Liquids --> "
        for group2 in range(0, len(ALL)):
            total = fsa_weight(transitions, ALL[group], ALL[group2])
            weights[group2] = total
            string2 = string
            if group2 == 0: string2 += "Vowels"
            if group2 == 1: string2 += "Stops"
            if group2 == 2: string2 += "Fricatives"
            if group2 == 3: string2 += "Liquids"
            next_state_strs.append(string2)
            #print string2 + ": " + str(total)
        for x in range(0, len(ALL)):
            print next_state_strs[x] + ": " + str(weights[x] / weights.sum())

def print_all_features(transitions, FEAT = FEAT):
    weights = numpy.zeros(len(ALL))
    for group in range(0, len(ALL)):
        string = ""
        next_state_strs = []
        if group == 0: string += "Vowels --> "
        if group == 1: string += "Stops --> "
        if group == 2: string += "Fricatives --> "
        if group == 3: string += "Liquids --> "
        for group2 in range(0, len(FEAT)):
            total = fsa_weight(transitions, FEAT[group2], ALL[group])
            weights[group2] = total
            string2 = string
            if group2 == 0: string2 += "__BOS__"
            if group2 == 1: string2 += "__EOS__"
            next_state_strs.append(string2)
        for x in range(0, len(FEAT)):
            print next_state_strs[x] + ": " + str(weights[x] / weights.sum())

def bos(transitions):
    weights = numpy.zeros(len(ALL))
    strs = []
    for group in range(0, len(ALL)):
        string = ""
        if group == 0: string += "Vowels --> "
        if group == 1: string += "Stops --> "
        if group == 2: string += "Fricatives --> "
        if group == 3: string += "Liquids --> "
        strs.append(string)
        weights[group] = fsa_weight(transitions, FEAT[0], ALL[group])
    for x in range(0, len(ALL)):
        print strs[x] + "BOS: " + str(weights[x] / weights.sum())
        
        
        

def normalize(transitions):
    """Normalizes the crf weights

    Args:
        transitions: a list of transition 3-tuples-(feature, state, weight)
    Returns:
        a list of transition 3-tuples normalized-(feature, state, probability)

    """
    current = 0 #the current transition 3-tuple
    nxt = 0
    new_transitions = []
    while current != len(transitions):
        current_name = transitions[current][0]
        weights = []
        total = 0
        for x in range(current, len(transitions)):
            if transitions[x][0] == current_name:
		weights.append(transitions[x])
		#print x, transitions[x]
		nxt = x
	    else:
		break
        for x in weights:
            total += float(x[2])
        for x in weights:
            new_transitions.append((x[0], x[1], float(x[2]) / total))
        current = nxt + 1
    return new_transitions
    
        
            
            
        
    
    
    
        
        
    
